package it.dk.view;

import it.dk.model.CollisionManager;

import java.util.Random;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;

/**
 * Classe che rappresenta lo stato di demo.
 * 
 * Pattern utilizzato: State
 */
public class GameStateDemo extends GameStatePlaying {
	private final int MAX_BARRELS_TO_JUMP = 10;
	private final int DISTANCE_FROM_BARREL_TO_JUMP_X = 100;
	private final int DISTANCE_FROM_BARREL_TO_JUMP_Y = 15;
	private BoardRender boardRender;
	private GameSound soundManager;
	private boolean isUpKey;
	private boolean isDownKey;
	private boolean isLeftKey;
	private boolean isRightKey;
	private int leftOrRight;
	private boolean jumpingRight;
	private boolean jumpingLeft;
	private Random random;
	private int randomJump;
	private boolean changeDirectionRight;
	private boolean changeDirectionLeft;
	private boolean lastPlatform;

	/**
	 * Costruttore della classe
	 * 
	 * @param board	riferimento alla board di gioco
	 */
	public GameStateDemo(DonkeyKong board) {
		super(board);
		soundManager = GameSound.getInstance();
		soundManager.playDemoSoundtrack();
		isUpKey = false;
		isDownKey = false;
		isLeftKey = false;
		isRightKey = false;
		changeDirectionRight = false;
		changeDirectionLeft = false;
		random = new Random();
		randomJump = random.nextInt(MAX_BARRELS_TO_JUMP);
		lastPlatform = false;
	}


	/**
	 * Metodo che sovrascrivendo il corrispondente metodo della classe
	 * GameStatePlaying, gestisce l'update dell'eroe simulando l'input
	 * da tastiera dei comandi di movimento.
	 * 
	 * @param container	oggetto di tipo GameContainer che rappresenta la finestra contenente il gioco
	 * @param delta		periodo di aggiornamento
	 */
	protected void movementDetector(GameContainer container, int delta){
		/* Quando collido con la parte inferiore della piattaforma comincio a salire */
		if (collisionHeroBottomLadder!=CollisionManager.getNotCollision() && !jumping){
			hero.moveUp(delta);
			this.setUpKey();
			/* Una volta arrivato in cima alla scala decido la direzione in cui spostarmi in base alla piattaforma sulla quale sono salito */
		} else if (collisionHeroTopLadder!=CollisionManager.getNotCollision() && (!lastPlatform||collisionHeroPlatform==6)) {

			if (collisionHeroPlatform!=CollisionManager.getNotCollision() && (collisionHeroPlatform%2!=0||collisionHeroPlatform==6) && collisionHeroTopLadder!=CollisionManager.getNotCollision()) {
				hero.moveLeft(delta);
				this.setLeftKey();
				leftOrRight = -1;
				changeDirectionLeft = false;	
			}else{
				hero.moveRight(delta);
				this.setRightKey();
				leftOrRight = 1;
				changeDirectionRight = false;	
			}


			/* Quando sto gi� su di una scala e non sto saltando, posso continuare a salire sulla scala */
		} else if (collisionHeroLadder!=CollisionManager.getNotCollision() && !jumping && collisionHeroPlatformUpBorder==CollisionManager.getNotCollision()) {

			hero.moveUp(delta);
			this.setUpKey();
			/* Controllo se sto camminando su di una piattaforma e se questa � dispari, e poi decido se andare a destra o a sinistra */
		}else if (collisionHeroPlatform!=CollisionManager.getNotCollision() && collisionHeroPlatform%2!=0) { 
			if(!changeDirectionLeft){
				hero.moveLeft(delta);
				this.setLeftKey();

			}else{
				hero.moveRight(delta);
				this.setRightKey();

			}

			/* Controllo se sto camminando su di una piattaforma e se questa � pari, e poi decido se andare a destra o a sinistra */
		}else if (collisionHeroPlatform!=CollisionManager.getNotCollision() && collisionHeroPlatform%2==0) { 
			if(!changeDirectionRight && collisionHeroPlatform!=6){
				hero.moveRight(delta);
				this.setRightKey();

			}else{
				hero.moveLeft(delta);
				this.setLeftKey();

			}

		}


		/* Controllo collisione con il bordo sinistro e cambio direzione */
		if(collisionHeroLeftBorder!=CollisionManager.getNotCollision()){
			changeDirectionLeft = true;
			changeDirectionRight = false;
			leftOrRight=1;

			if(collisionHeroPlatform==5){
				lastPlatform = true;
			}	
		}


		/* Controllo collisione con il bordo destro e cambio direzione */
		if(collisionHeroRightBorder!=CollisionManager.getNotCollision()){
			changeDirectionRight = true;
			changeDirectionLeft = false;
			leftOrRight=-1;
		}


		/* Controllo quale barile devo saltare, controllo i primi n barili, dove n � un numero generato a caso all'avvio della demo
		 * Inoltre per decidere quando saltare controllo la distanza dal barile lungo x e lungo y e se mi muovo nella direzione contro il barile */
		for(int i = 0;i<randomJump;i++){
			if (!isUpKey && Math.abs(hero.getShape().getY()- board.getGame().getBarrels().get(i).getRound().getY()) <= DISTANCE_FROM_BARREL_TO_JUMP_Y
					&& Math.abs(hero.getShape().getX()- board.getGame().getBarrels().get(i).getRound().getX()) <= DISTANCE_FROM_BARREL_TO_JUMP_X
					&& ((hero.getShape().getX()>board.getGame().getBarrels().get(i).getRound().getX() && leftOrRight==-1)||(hero.getShape().getX()<board.getGame().getBarrels().get(i).getRound().getX() && leftOrRight==1))) {

				/* Se leftOrRight � pari a uno vuol dire che il mio salto dovr� essere verso destra */
				if (leftOrRight == 1) {
					jumpingRight = true;
					jumpingLeft = false;
				} else {
					jumpingLeft = true;
					jumpingRight = false;
				}
				hero.jump(delta, leftOrRight);
				this.setJumpKey();
				s.setJumping(true);
				jumping = true;
			}
		}


		if(jumping){
			hero.moveDown(delta);
			this.setDownKey();
		}

	}

	/**
	 * Metodo che viene richiamato da Board per
	 * il render degli oggetti grafici
	 * 
	 * @param container	oggetto di tipo GameContainer che rappresenta la finestra contenente il gioco
	 * @param g			oggetto di tipo Graphics che consente la renderizzazione di un generioco oggetto grafico
	 */
	public void render(GameContainer container, Graphics g) {
		boardRender = BoardRender.getInstance();
		super.render(container, g);
		boardRender.drawDemoScreen(g);
	}


	/**
	 * Metodo che sovrascrivendo il corrispondente metodo della classe
	 * GameStatePlaying gestisce il render dell'eroe coerentemente con
	 * la movimentazione ricevuta dal simulatore.
	 * 
	 * @param container	oggetto di tipo GameContainer che rappresenta la finestra contenente il gioco
	 * @param g			oggetto di tipo Graphics che consente la renderizzazione di un generioco oggetto grafico
	 */
	protected void renderMovement(GameContainer container, Graphics g){
		if(isUpKey && !jumping){
			boardRender.renderHeroClimbing(g);
		}else if(isDownKey && !jumping){
			boardRender.renderHeroClimbing(g);
		}else if(isLeftKey){
			boardRender.renderHeroRunningLeft(g);
		}else if(isRightKey){
			boardRender.renderHeroRunningRight(g);
		}else if(jumpingRight){
			boardRender.renderHeroJumpingRight(g);

		}else if(jumpingLeft){
			boardRender.renderHeroJumpingLeft(g);
		}else{
			/* Se non sono verificate tutte le altre condizioni, sto cadendo nel vuoto */
			boardRender.renderHeroJumping(g);
		}

	}
	
	/**
	 * Metodo che rileva il rilascio di un tasto
	 * 
	 * @param key	codice del tasto rilasciato
	 * @param c		carattere associato al tasto rilasciato
	 * 
	 */
	@Override
	public void keyReleased(int key, char c) {
		switch(key) {
		default:
			board.resetGameState();
			board.newGame();
			break;
		}
	}

	/**
	 * Metodo setter del flag per segnalare la movimentazione verso l'alto dell'eroe
	 */
	private void setUpKey(){
		isUpKey = true;
		isDownKey = false;
		isLeftKey = false;
		isRightKey = false;
	}

	/**
	 * Metodo setter del flag per segnalare la movimentazione verso il basso dell'eroe
	 */
	private void setDownKey(){
		isUpKey = false;
		isDownKey = true;
		isLeftKey = false;
		isRightKey = false;
	}


	/**
	 * Metodo setter del flag per segnalare la movimentazione verso sinistra dell'eroe
	 */
	private void setLeftKey(){
		isUpKey = false;
		isDownKey = false;
		isLeftKey = true;
		isRightKey = false;
	}

	/**
	 * Metodo setter del flag per segnalare la movimentazione verso destra dell'eroe
	 */
	private void setRightKey(){
		isUpKey = false;
		isDownKey = false;
		isLeftKey = false;
		isRightKey = true;
	}

	/**
	 * Metodo setter del flag per segnalare la movimentazione di salto dell'eroe
	 */
	private void setJumpKey(){
		isUpKey = false;
		isDownKey = false;
		isLeftKey = false;
		isRightKey = false;
	}
}
